﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace LinqToDB.Provider
{
    class SqlNodeConvertor:ExpressionVisitor
    {
        public SqlNodeConvertor()
        { }


        SqlNode root;
        Stack<SqlDefault> stack = new Stack<SqlDefault>();

        public SqlNode Convert(Expression source)
        {
            SqlNodeConvertor convert = new SqlNodeConvertor();
            convert.Visit(source);
            return convert.root;
        }

        void Begin(string name, object value,Expression source)
        { 
            SqlDefault node = new SqlDefault(name, value, source);
            if (stack.Count > 0)
            {
                SqlDefault parent = stack.Peek();
                parent.SubNodes.Add(node);
            }
            else
                root = node;

            stack.Push(node);
        }
        void End(string name, object value)
        {
            stack.Pop();
        }

        internal override Expression VisitLambda(LambdaExpression lambda)
        {
            Begin("Lambda", null, lambda);
            var r = base.VisitLambda(lambda);
            End("Lambda", null);
            return r;
        }

        internal override Expression VisitConstant(ConstantExpression c)
        {
            if (c.Value is ITable)
            {
                Begin("Table", ((ITable)c.Value),c);
                var r = base.VisitConstant(c);
                End("Table", ((ITable)c.Value));
                return r;
            }
            else
            {
                Begin("Constant", c.Value,c);
                var r = base.VisitConstant(c);
                End("Constant", c.Value);
                return r;
            }
        }

        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method.DeclaringType == typeof(Queryable))
            {
                return VisitQueryableMethod(m);
            }

            Begin("MethodCall", m.Method.Name, m);
            var r = base.VisitMethodCall(m);
            End("MethodCall", m.Method.Name);
            return r;
        }

        internal override Expression VisitParameter(ParameterExpression p)
        {
            Begin("Parameter", p.Name,p);
            var r = base.VisitParameter(p);
            End("Parameter", p.Name);
            return r;
        }

        internal override NewExpression VisitNew(NewExpression nex)
        {
            Begin("New", nex.Constructor.DeclaringType.Name,nex);
            var r = base.VisitNew(nex);
            End("New", nex.Constructor.DeclaringType.Name);
            return r;
        }
        internal override Expression VisitMemberAccess(MemberExpression m)
        {
            Begin("MemberAccess", m.Member.Name,m);
            var r = base.VisitMemberAccess(m);
            End("MemberAccess", m.Member.Name);
            return r;
        }

        internal override Expression VisitBinary(BinaryExpression b)
        {
            Begin("Binary", b.NodeType,b);
            var r = base.VisitBinary(b);
            End("Binary", b.NodeType);
            return r;
        }

        internal override Expression VisitUnary(UnaryExpression u)
        {
            if (u.NodeType == ExpressionType.Quote)
                return base.VisitUnary(u);

            Begin("Unary", u.NodeType, u);
            var r = base.VisitUnary(u);
            End("Unary", u.NodeType);
            return r;
        }

        Expression VisitQueryableMethod(MethodCallExpression m)
        {
            switch (m.Method.Name)
            {
                case "Select":
                case "SelectMany":
                case "Where":
                case "Group":
                    break;
            }

            Begin(m.Method.Name,null,m);
            var r = base.VisitMethodCall(m);
            End(m.Method.Name,null);
            return r;
        }

        Expression VisitSelect(MethodCallExpression m)
        {
            Begin(m.Method.Name, null, m);
            var r = base.VisitMethodCall(m);
            End(m.Method.Name, null);
            return r;
        }

    }
}
